// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Diagnostics.Tracing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Tools.EtlReader;

namespace EventsValidationTest
{

    extern alias ServiceFabricInternal;
    using FabricEventsInternal = ServiceFabricInternal::System.Fabric.Common.Tracing.FabricEvents;

    [TestClass]
    public class EventsValidation
    {
        [TestMethod]
        [Owner("asnegi,raunakp")]
        public void UniqueEventIdsAndTaskOpcodePairsPerEventSource()
        {
            foreach (var eventSource in EventSourcesToValidate)
            {
                HashSet<int> eventIds = new HashSet<int>();
                HashSet<Tuple<EventTask, EventOpcode>> taskOpCodePairs = new HashSet<Tuple<EventTask, EventOpcode>>();

                var methodTypeFlags = BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.FlattenHierarchy|BindingFlags.Public;
                foreach (var eventMethod in eventSource.GetType().GetMethods(methodTypeFlags))
                {
                    const bool followInheritanceChain = true;
                    EventAttribute eventAttribute = (EventAttribute)eventMethod.GetCustomAttributes(typeof(EventAttribute), followInheritanceChain).SingleOrDefault();

                    if (eventAttribute == null)
                    {
                        continue;
                    }

                    if (eventIds.Contains(eventAttribute.EventId))
                    {
                        throw new Exception(string.Format("Duplicate Event Id {0} in {1}", eventAttribute.EventId, eventSource));
                    }

                    eventIds.Add(eventAttribute.EventId);

                    if (eventAttribute.Task == EventTask.None)
                    {
                        // If task is none, it is generated by eventsource and is unique.
                        continue;
                    }

                    var taskOpcodePair = new Tuple<EventTask, EventOpcode>(eventAttribute.Task, eventAttribute.Opcode);
                    if (taskOpCodePairs.Contains(taskOpcodePair))
                    {
                        throw new Exception(string.Format("Duplicate Task {0} and Opcode {1} pair in {2}", eventAttribute.Task, eventAttribute.Opcode, eventSource));
                    }

                    taskOpCodePairs.Add(taskOpcodePair);
                }

                Console.WriteLine("No of events checked : {0} in {1}", eventIds.Count(), eventSource);
                Assert.IsTrue(eventIds.Count() > 0 , string.Format("No events found in {0}", eventSource));
            }
        }

        [TestMethod]
        [Owner("asnegi,raunakp")]
        public void AllEventSourcesCoveredForTest()
        {
            var allGuids = WellKnownProviderList.WellKnownGuids.ToList().Concat(WellKnownProviderList.WellKnownDynamicProviderGuids);
            var shouldValidateGuids = allGuids.Except(GuidsToIgnore);
            var currentValidationGuidList = EventSourcesToValidate.Select((eventSource) => eventSource.Guid);
            var newlyAddedGuids = shouldValidateGuids.Except(currentValidationGuidList);
            if (newlyAddedGuids.Count() > 0)
            {
                throw new Exception(string.Format("New guids {0} are missing in EventSourcesToValidate list. Add them in EventsValidation.cs file.",
                    string.Join(", ", newlyAddedGuids)));
            }
        }

        // List of eventsources for which validation need to be done.
        static readonly HashSet<EventSource> EventSourcesToValidate = new HashSet<EventSource> {
            System.Fabric.Common.Tracing.FabricEvents.Events,
            Microsoft.ServiceFabric.Services.Runtime.ServiceFrameworkEventSource.Writer,
            Microsoft.ServiceFabric.Services.ServiceEventSource.Instance,
            Microsoft.ServiceFabric.Actors.Diagnostics.ActorFrameworkEventSource.Writer,
            Microsoft.ServiceFabric.Actors.ActorEventSource.Instance,
            Microsoft.ServiceFabric.Diagnostics.Tracing.ServiceFabricStringEventSource.Instance,
            FabricEventsInternal.Events
        };

        // List of non-managed-code / test eventsources for which validation can't be done.
        static readonly Guid[] GuidsToIgnore = new Guid[]
        {
            // Guids used in testing ; some are not even present in WindowsFabric repo.
            new Guid("{05A7BE1B-AC8A-4920-B78E-6B371E1CCED2}"), // test GUID
            new Guid("{fc9e797f-436c-44c9-859e-502bbd654403}"), // test GUID
            new Guid("{6c955abf-b2ad-4236-a033-00f9c978e0e7}"), // test GUID
            new Guid("{C083AB76-A7F3-4CA7-9E64-CA7E5BA8807A}"), // DCA AzureTableUploader CIT
            // Native event source.
            new Guid("{3f68b79e-a1cf-4b10-8cfd-3dfe322f07cb}"), // Lease
            new Guid("{E658F859-2416-4AEF-9DFC-4D303897A37A}"), // KTL
            new Guid("{c4766d6f-5414-5d26-48de-873499ff0976}"),  // for ServiceFabricHttpClientEventSource (SF-ClientLib)
            new Guid("{ecec9d7a-5003-53fa-270b-5c9f9cc66271}"),  // for ServiceFabric.ReliableCollection
            // POA event source. Not in code base.
            new Guid("{24afa313-0d3b-4c7c-b485-1047fd964b60}"),  // for SF Patch Orchestration Application CoordinatorService
            new Guid("{e39b723c-590c-4090-abb0-11e3e6616346}"),  // for SF Patch Orchestration Application NodeAgentService
            new Guid("{fc0028ff-bfdc-499f-80dc-ed922c52c5e9}"),  // for SF Patch Orchestration Application NodeAgentNTService
            new Guid("{05dc046c-60e9-4ef7-965e-91660adffa68}"),  // for SF Patch Orchestration Application NodeAgentSFUtility

        };

    }
}